// includes, system
#include <aithin_cuda.h>

// Simple utility function to check for CUDA runtime errors
void checkCUDAError(const char *msg)
{
    cudaError_t err = cudaGetLastError();
    if( cudaSuccess != err) 
    {
        fprintf(stderr, "Cuda error: %s: %s.\n", msg, 
                             cudaGetErrorString( err) );
        exit(EXIT_FAILURE);
    }                         
}
////////////////////////////////////////////////////////////////////
// Program test
/////////////////////////////////////////////////////////////////////
 extern "C" __declspec(dllexport) int end_check_embryos(
			int* dev_neurons,
			cuEmbryo* embryos,
            cuEmbryo* dev_embryos, 
            int embryos_count)
{
	printf("embryos: %d \n", embryos_count);

      size_t memSize_emby = embryos_count * sizeof(cuEmbryo);

    // block until the device has completed
    cudaThreadSynchronize();

    // check if kernel execution generated an error
    // Check for any CUDA errors
    checkCUDAError("kernel invocation");

    // device to host copy
	cudaMemcpy( embryos, dev_embryos, memSize_emby, cudaMemcpyDeviceToHost );

    // Check for any CUDA errors
    checkCUDAError("memcpy");

    // free device memory
    cudaFree(dev_embryos);
	cudaFree(dev_neurons);

    // If the program makes it this far, then results are correct and
    // there are no run-time errors.  Good work!
//    printf("Correct!\n");

    return 0;
}

 extern "C" __declspec(dllexport) cuPointers  check_embryos(int* neurons,
            int neurons_count,
            cuEmbryo* embryos, 
            int embryos_count)
{
	printf("embryos: %d \n", embryos_count);

    // pointer for device memory
	cuEmbryo* dev_embryos;
	int* dev_neurons;

	//pointers for callback
	cuPointers dev_ptr;

    // define grid and block size
    int numThreadsPerBlock = 256;

	int numBlocks_e = embryos_count/numThreadsPerBlock +1;

    // allocate host and device memory
    size_t memSize_neurons = neurons_count * sizeof(int);
    size_t memSize_emby = embryos_count * sizeof(cuEmbryo);

	// allocate device memory for embryos
	cudaMalloc( (void **) &dev_neurons, memSize_neurons );
	cudaMalloc( (void **) &dev_embryos, memSize_emby );

	dev_ptr.device_neurons = dev_neurons;
	dev_ptr.device_embryos = dev_embryos;

    // Copy host array to device array
    cudaMemcpy( dev_neurons, neurons, memSize_neurons, cudaMemcpyHostToDevice );
    cudaMemcpy( dev_embryos, embryos, memSize_emby, cudaMemcpyHostToDevice );


    // launch kernel
    dim3 dimGrid(numBlocks_e);
    dim3 dimBlock(numThreadsPerBlock);

	seek_embryos <<< dimGrid, dimBlock>>> 
		( dev_neurons,
		neurons_count,
		dev_embryos, 
		embryos_count);

    // check if kernel execution generated an error
    // Check for any CUDA errors
    checkCUDAError("kernel invocation");

//    printf("Correct!\n");

    return dev_ptr;
}
/*
/////////////////////////////////////////////////////////////////////
// Program main
/////////////////////////////////////////////////////////////////////
int main( int argc, char** argv) 
{
	int* a;
	for (int i = 0; i < 3; i++)
    {
        assert(h_a[i] == dimA - 1 - i );
    }
    check_embryos(a,3);

    return 0;
}
*/